/*
* tmf_measure.c
*/

#include "tmf_measure.h"

struct tmf_msr_mng_t g_tmf_msr_mng;

/*
* @name tmf_msr_timer_thrd
* Timer thread of reporting temperature. Callback function called in this thread.
* @ input:
*		pparam: pointer of thread param.
* @ output: Nono.
* @ return: Nono.
*/
void * tmf_msr_timer_thrd(void* pparam)
{
	int ret;
	float tmpr;
	time_t tm;
	struct timespec tmspec;
	struct tmf_msr_mng_t * mng = (struct tmf_msr_mng_t *)pparam;
	
	while(!mng->timer_thrd_exit) {

		pthread_mutex_lock(&mng->mtx_msr_mode);

		if(TMF_MEASURE_MODE_PASSIVE == mng->mode) {
			/* We are in passive mode, so it's going to suspend till being waken up. */
			pthread_cond_wait(&mng->cond_msr_mode, &mng->mtx_msr_mode);
		} else if(TMF_MEASURE_MODE_AUTO == mng->mode) {
			/* We are in auto mode, so we have to invoke callback function in every interval. */
			tmspec.tv_sec = time(NULL) + mng->interval;
			tmspec.tv_nsec = 0;
			/* We must notice that timer thread shouldn't sleep all the time, since
			* there are some signal which couldn't be ignored sending to it.
			*/
			ret = pthread_cond_timedwait(&mng->cond_msr_mode, &mng->mtx_msr_mode, &tmspec);
			if(ETIMEDOUT == ret) {
				if(mng->tmf_msr_notify_cb != NULL) {
					tmf_msr_get_tmpr(&tmpr, &tm);
					mng->tmf_msr_notify_cb(tmpr, tm);
				}
			} else if(0 == ret) {
				/* We can handle some cases here!
				* For example: 
				* 1.Exit signal has been send to me.
				* 2.User called tmf_msr_set_mode to reset mode of getting temperature.
				* Whatever, we can respond to the caller immediately!
				*/
				;
			} else {
				;
			}
		} else {
			/* Never be here! Invalid mode. */
			;
		}
		
		pthread_mutex_unlock(&mng->mtx_msr_mode);
	}
	return NULL;
}
	
/*
* @name tmf_msr_init
* Initialize Measure module.
* @input:
*		argc: parameter number.
*		argv: vector of parameter pointer.
* @output: Nono.
* @return:
*		0 - Success.
*		-1 - Open device file is failed.
*		-2 - Create thread is failed.
*/
int tmf_msr_init(int argc, char * argv[])
{
	int ret = 0;

	g_tmf_msr_mng.timer_thrd_exit = 0;
	g_tmf_msr_mng.mode = TMF_MEASURE_MODE_PASSIVE;
	g_tmf_msr_mng.interval = 0;
	g_tmf_msr_mng.tmf_msr_notify_cb = NULL;
	if((g_tmf_msr_mng.fd = open("/dev/ds18b20", O_RDWR | O_NDELAY | O_NOCTTY)) < 0) {
		ret = -1;
		goto out;
	}
	if(0 != pthread_create(&g_tmf_msr_mng.timer_thrd_id, NULL, tmf_msr_timer_thrd, (void *)&g_tmf_msr_mng)) {
		ret = -2;
		goto err;
	}
	pthread_mutex_init(&g_tmf_msr_mng.mtx_msr_mode, NULL);
	pthread_cond_init(&g_tmf_msr_mng.cond_msr_mode, NULL);
out:
	return ret;
err:
	close(g_tmf_msr_mng.fd);
	return ret;
}

/*
* @name tmf_msr_deinit
* Deinitialize Measure module
* @input: Nono.
* @output: Nono.
* @return: Nono.
*/
void tmf_msr_deinit(void)
{
	if(0 != g_tmf_msr_mng.timer_thrd_id) {
		
		g_tmf_msr_mng.timer_thrd_exit = 1;
		pthread_cond_signal(&g_tmf_msr_mng.cond_msr_mode);
		pthread_join(g_tmf_msr_mng.timer_thrd_id, (void **)NULL);
	}
	if(-1 != g_tmf_msr_mng.fd) {
		close(g_tmf_msr_mng.fd);
	}
	pthread_mutex_destroy(&g_tmf_msr_mng.mtx_msr_mode);
	pthread_cond_destroy(&g_tmf_msr_mng.cond_msr_mode);
}


/*
* @name tmf_msr_get_tmpr.
* Get current temperature and time.
* @input: Nono.
* @output:
*		tmpr: temperature value.
*		time: current time.
* @return:
*		0 - Success.
*		-1 - File handle of device is invalid.
*		-2 - Get temperature by reading from device file is failed.
*/
int tmf_msr_get_tmpr(float * tmpr, time_t * tm)
{
	int ret;
	unsigned char buf[2];

	if(-1 != g_tmf_msr_mng.fd) {
		if(read(g_tmf_msr_mng.fd, buf, 1) > 0){
			*tmpr = (float)buf[0];
			*tmpr /= 16;
			*tmpr += ((float)buf[1] * 16);
			time(tm);
			ret = 0;
		} else {
			perror("tmf_msr_get_tmpr");
			ret = -1;
		}
	} else {
		ret = -2;
	}
	return ret;
}

/*
* @name tmf_msr_set_mode
* Set mode of getting temperature. There are two modes: TMF_MEASURE_MODE_PASSIVE and
* TMF_MEASURE_MODE_AUTO. If TMF_MEASURE_MODE_PASSIVE mode is set, the caller should
* invoke tmf_msr_get_tmpr to get current temperature and parameter interval is ignored.
* If TMF_MEASURE_MODE_AUTO is set, a callback function should been assigned (see tmf_msr_set_notify_callback)
* by caller and being called in interval (in second).
* 
* @input:
*		mode: mode of getting temperature.
*		interval: interval of reporting temperature (in second).
* @output: Nono.
* @return:
*		0 - Success.
*		-1 - Parameter mismatch.
*		-2 - Inner error.
*/
int tmf_msr_set_mode(enum TMF_MSR_MODE mode, unsigned int interval)
{
	int ret = 0;
	
	pthread_mutex_lock(&g_tmf_msr_mng.mtx_msr_mode);
	if(g_tmf_msr_mng.mode == mode) {
		goto out;
	}
	/* Be careful for every situation! */
	if((TMF_MEASURE_MODE_AUTO == g_tmf_msr_mng.mode) &&
		(TMF_MEASURE_MODE_PASSIVE == mode)) {

		g_tmf_msr_mng.mode = mode;
		pthread_cond_signal(&g_tmf_msr_mng.cond_msr_mode);
	} else if((TMF_MEASURE_MODE_PASSIVE == g_tmf_msr_mng.mode) &&
			(TMF_MEASURE_MODE_AUTO == mode)) {
		if(0 == interval) {
			ret = -1;
		} else {
			g_tmf_msr_mng.interval = interval;
			g_tmf_msr_mng.mode = mode;
			pthread_cond_signal(&g_tmf_msr_mng.cond_msr_mode);
		}
	} else {
		/* Never be here! */
		ret = -2;
	}
out:
	pthread_mutex_unlock(&g_tmf_msr_mng.mtx_msr_mode);
	return ret;
}

/*
* @name tmf_msr_get_mode
* Get mode of getting temperature.
* 
* @input: None.
* @output:
*		mode - mode of getting temperature.
* @return: None.
*/
void tmf_msr_get_mode(enum TMF_MSR_MODE * mode)
{
	pthread_mutex_lock(&g_tmf_msr_mng.mtx_msr_mode);
	*mode = g_tmf_msr_mng.mode;
	pthread_mutex_unlock(&g_tmf_msr_mng.mtx_msr_mode);
}

/*
* @name tmf_msr_set_notify_callback.
* Set callback function. It will be called in interval (in microsecond) given
* by tmf_msr_set_mode.
* 
* @input:
*		tmf_notify_cb: callback function.
* @output: Nono.
* @return: Nono.
*/
void tmf_msr_set_notify_callback(TMF_MSR_NOTIFY_CB tmf_msr_notify_cb)
{
	g_tmf_msr_mng.tmf_msr_notify_cb = tmf_msr_notify_cb;
}

